package multi_thread.master.executors;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Cheng Jun
 * Description: 阅读 ThreadPoolExecutor 的类注释，重点内容
 * <p>
 * 一. Core and maximum pool sizes: 核心线程数，最大线程数， 额外的线程数 = 最大线程数 - 核心线程数
 * <p>
 * 二. Creating new threads: 创建新的线程规则：
 * 1.如果核心线程数没有达到设定值，有任务来了，就去创建核心线程，即使有可用的核心线程也不会用。
 * 2.核心线程数达到设定值，有任务来了，如果有核心线程空闲就用核心线程，如果没有的话，先放到 workQueue 中，
 * 如果 workQueue 放不下了，再去创建额外的线程；
 * 3.额外的线程也达到了最大值，就会根据 Rejected tasks 拒绝策略拒绝任务。
 * <p>
 * 三. Keep-alive times: 最大线程数减去核心线程数就是空闲线程，空闲线程在空闲情况下的存活时间。
 * 注意核心线程默认情况下，是不可回收的。但可以设置成回收
 * <p>
 * 四. Queuing: 阻塞队列——用来存放核心线程无法及时处理的任务
 * <p>
 * 五. Rejected tasks: 队列满了且活跃线程数达到最大线程数市，任务被提交后。线程池的拒绝策略
 * 1.AbortPolicy 抛出 RejectedExecutionException，拒绝服务
 * 2.DiscardPolicy 悄无声息得丢弃 无法入列的任务
 * 3.DiscardOldestPolicy 悄无声息得丢弃 已经在队列中的任务，腾出位置给后来的任务
 * 4.CallerRunsPolicy 被拒绝任务的处理程序，它直接在execute方法的调用线程中运行被拒绝的任务。任务不会掉，但是会瞬间消耗大量 CPU资源
 * 线程池:
 * @version 1.0
 * @date 2022/5/18 22:28
 * @see ThreadPoolExecutor
 * 思考：如何移除 workQueue 中的任务，如何中止正在运行的任务？请看 RemoveRunnableFutureExample
 * @see RemoveRunnableFutureExample
 * 思考：如果提交了 Callable，如何获取对应的结果？ 请看 ExecutorCompletionServiceExample
 * @see ExecutorCompletionServiceExample
 */
public class Executor_CustomedThreadPool {

    public static void main(String[] args) throws InterruptedException {
        // 指定数组大小的 阻塞队列
        ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor myTPExecutor =
                new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS, workQueue,
                        new MyThreadFactory("自定义线程池"),
                        new ThreadPoolExecutor.CallerRunsPolicy());

        // 设置核心线程 是否超时关闭，默认为false——核心线程不会超时关闭
        // myTPExecutor.allowCoreThreadTimeOut(true);

        // allowsCoreThreadTimeOut 返回核心线程是否超时关闭的设置，
        // 注意 allowCoreThreadTimeOut(boolean value) 和 allowsCoreThreadTimeOut 的区别
        // System.out.println("核心线程是否超时关闭：" + myTPExecutor.allowsCoreThreadTimeOut());

        System.out.println("myTPExecutor.getPoolSize() 线程池中的线程数量：" + myTPExecutor.getPoolSize());
        // System.out.println("myTPExecutor.prestartCoreThread 预启动一个核心线程是否成功：" + myTPExecutor.prestartCoreThread());
        System.out.println("myTPExecutor.prestartAllCoreThreads 预启动所有核心线程：" + myTPExecutor.prestartAllCoreThreads());
        System.out.println("myTPExecutor.getPoolSize() 线程池中的线程数量：" + myTPExecutor.getPoolSize());

        for (int i = 0; i < 1; i++) {
            int number = i;
            Callable task = () -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("当前线程：" + Thread.currentThread().getName());
                    // 演示未捕获的异常
                    int res = 10 / 0;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " number：" + number);
                return "执行成功";
            };
            // myTPExecutor.execute(task);

            // 注意：submit 提交的任务，无法利用到自定义线程工厂设置的 未捕获异常的处理方式；
            // submit会吞掉异常，可通过Future的get方法将任务执行时的异常重新抛出。
            Future future = myTPExecutor.submit(task);
            try {
                // future.get() 会阻塞当前线程的运行，只是测试的时候这样写一下，开发过程中，切记不能这样写
                // get(long timeout, TimeUnit unit) 也有带时间的阻塞方法，开发场景使用频率更高，因为用户不能一直等运行结果，
                // 如果超过了指定时间，就要返回提示信息
                System.out.println(future.get());
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            // 任务可以多次提交，这里要注意Executor的本质是提供了 将任务提交与每个任务如何执行的机制分离的方法
            // Executor 的类注释：This interface provides a way of decoupling task submission from the mechanics of
            // how each task will be run, including details of thread use, scheduling, etc.
            // myTPExecutor.execute(task);

        }
        // 关闭线程池，不接受新的任务，已经提交的任务会被继续执行完。如果已经被关闭了，再次调用没有任何效果。
        myTPExecutor.shutdown();

        // 马上关闭线程池，workQueue 中未完成的任务会被返回。
        // 正在执行的任务，如果捕获 InterruptedException，就能被打断；反之，则不能被打断
        // myTPExecutor.shutdownNow();

        // 只要先调用了 shutdown() 或 shutdownNow()，isShutdown() 就会返回 true
        // System.out.println(myTPExecutor.isShutdown());

        // 只要先调用了 shutdown() 或 shutdownNow()，并且所有的任务都结束了（无论正常结束还是异常结束），两者都满足，才会返回true
        // System.out.println(myTPExecutor.isTerminated());


        try {
            while (!myTPExecutor.awaitTermination(100, TimeUnit.MILLISECONDS)) {
                System.out.println("只要有任务没结束就死循环！");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 参考 Executors.DefaultThreadFactory 自定义线程工厂，主要是为了自定义线程名称，方便区分和定位问题。
     * 并自定义未捕获异常的处理方式
     *
     * @author Cheng Jun
     * @date 2022/5/21 10:23
     * @see Executors.DefaultThreadFactory
     */
    static class MyThreadFactory implements ThreadFactory {

        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
        private final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
        private final String NAME_PREFIX;

        MyThreadFactory(String name) {
            if (null == name || "".equals(name.trim())) {
                name = "pool";
            }
            NAME_PREFIX = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable target) {
            String name = NAME_PREFIX + THREAD_NUMBER.getAndIncrement();
            Thread thread = new Thread(target, name);
            if (thread.isDaemon()) {
                thread.setDaemon(false);
            }
            if (thread.getPriority() != Thread.NORM_PRIORITY) {
                thread.setPriority(Thread.NORM_PRIORITY);
            }
            // 自定义未捕获的异常处理方式
            thread.setUncaughtExceptionHandler((Thread t, Throwable e) -> {
                // 控制台打印异常信息
                e.printStackTrace();
                // 输出线程的异常信息
                System.out.println(t.getName() + e.getMessage());
            });
            return thread;
        }
    }
}
